package com.ruoyi.web.controller.common;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.VsUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.config.VsDramasConfig;
import com.ruoyi.system.domain.user.VsDramasUserOauth;
import com.ruoyi.system.service.config.IVsDramasConfigService;
import com.ruoyi.system.service.user.IVsDramasUserOauthService;
import com.ruoyi.system.service.user.IVsUserService;
import com.ruoyi.web.controller.pay.PayPalPaymentConfig;
import com.xkcoding.http.config.HttpConfig;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.scope.*;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * This file is part of 南阳迈特网络科技有限公司.
 * @company  南阳迈特网络科技有限公司
 * @link     https://www.nymaite.com
 * @document https://product.nymaite.cn
 * @contact  service@nymaite.com
 * @author nymaite
 */
@RestController
@RequestMapping("/oauth")
public class RestAuthController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(RestAuthController.class);

    @Autowired
    private IVsUserService userService;

    @Autowired
    private IVsDramasUserOauthService vsDramasUserOauthService;

    @Autowired
    private IVsDramasConfigService vsDramasConfigService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisCache redisCache;

    @RequestMapping("/render/{source}")
    @ResponseBody
    public AjaxResult renderAuth(
            @PathVariable("source") String source,
            @RequestParam("returnUrl") String returnUrl,
            @RequestParam("spm") String spm,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        log.info("进入render：" + source);
        AuthRequest authRequest = getAuthRequest(source);
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        log.info(authorizeUrl);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("url",authorizeUrl);
        String state = extractStateValue(authorizeUrl);
        String urlKey = "returnUrl_" + source + "_" + state;
        String spmKey = "spm_" + source + "_" + state;
        redisCache.setCacheObject(urlKey, returnUrl, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        if (spm != null && !spm.isEmpty()) {
            redisCache.setCacheObject(spmKey, spm, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        }
        if (!StringUtils.isEmpty(request.getHeader("Lang-Id"))) {
            Integer integer = Integer.valueOf(request.getHeader("Lang-Id"));
            String langKey = "lang_" + source + "_" + state;
            redisCache.setCacheObject(langKey, integer, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        }
        return ajax;
    }

    private String extractStateValue(String input) {
        String stateKey = "state=";
        int startIndex = input.indexOf(stateKey);
        if (startIndex == -1) {
            return null; // 如果字符串中找不到state=，返回null或者适当的错误处理
        }

        startIndex += stateKey.length(); // 调整索引，指向state值的开始位置
        int endIndex = input.indexOf('&', startIndex); // 在下一个&符号之前找到结束位置
        if (endIndex == -1) {
            endIndex = input.length(); // 如果没有找到下一个&，则结束位置为字符串末尾
        }

        return input.substring(startIndex, endIndex);
    }

    /**
     * oauth平台中配置的授权回调地址，以本项目为例，在创建github授权应用时的回调地址应为：http://127.0.0.1:8443/oauth/callback/facebook
     */
    @RequestMapping("/callback/{source}")
    public void login(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request, HttpServletResponse httpServletResponse) {
        log.info("进入callback：" + source + " callback params：" + JSONObject.toJSONString(callback));

        redisCache.redisTemplate.setKeySerializer(new StringRedisSerializer());

        if (!redisCache.hasKey("returnUrl_" + source + "_" + callback.getState())) {
            throw new RuntimeException("Routing information does not exist");
        }
        Object cacheObject = redisCache.getCacheObject("returnUrl_" + source + "_" + callback.getState());
        String returnUrl = cacheObject.toString();
        AuthRequest authRequest = getAuthRequest(source);
        log.info("--------------------已成功建立请求---------------------");
        AuthResponse response = authRequest.login(callback);
        log.info(JSONObject.toJSONString(response));
        if (response.ok()) {
            com.alibaba.fastjson2.JSONObject data = JSON.parseObject(JSONObject.toJSONString(response.getData()));
            VsDramasUserOauth vsDramasUserOauth = new VsDramasUserOauth();
            vsDramasUserOauth.setProvider(data.getString("source"));
            vsDramasUserOauth.setOpenid(data.getString("uuid"));
            List<VsDramasUserOauth> oauths = vsDramasUserOauthService.selectVsDramasUserOauthList(vsDramasUserOauth);
            VsUser vsUser = new VsUser();
            if (oauths.isEmpty()) {
                VsUser user = new VsUser();
                user.setProvider(data.getString("source"));
                if (redisCache.hasKey("spm_" + source + "_" + callback.getState())) {
                    user.setSpm(redisCache.getCacheObject("spm_" + source + "_" + callback.getState()).toString());
                }
                String langId = "";
                if (redisCache.hasKey("lang_" + source + "_" + callback.getState())) {
                    langId = redisCache.getCacheObject("lang_" + source + "_" + callback.getState()).toString();
                } else {
                    langId = "69";
                }
                vsUser = userService.registerUser(user, langId);
                VsDramasUserOauth oauth = new VsDramasUserOauth();
                oauth.setUserId(vsUser.getId());
                oauth.setProvider(data.getString("source"));
                oauth.setPlatform("H5");
                oauth.setOpenid(data.getString("uuid"));
                oauth.setNickname(data.getString("nickname"));
                oauth.setHeadimgurl(data.getString("avatar"));
                try {
                    ObjectMapper mapper = new ObjectMapper();
                    JsonNode rootNode = mapper.readTree(JSONObject.toJSONString(response.getData()));

                    // 获取accessToken
                    String accessToken = rootNode.path("data").path("token").path("accessToken").asText();
                    oauth.setAccessToken(accessToken);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                oauth.setCreatetime(System.currentTimeMillis());
                oauth.setUpdatetime(System.currentTimeMillis());
                vsDramasUserOauthService.insertVsDramasUserOauth(oauth);
            } else {
                vsUser = userService.selectVsUserById(oauths.get(0).getUserId());
            }
            Map map = userService.smsLogin(vsUser);

            com.alibaba.fastjson2.JSONObject userInfo = JSON.parseObject(JSONObject.toJSONString(map.get("user")));
            String url = returnUrl + "?userId=" + userInfo.get("id") + "&accessToken=" + map.get("token");
            try {
                httpServletResponse.sendRedirect(url);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        throw new RuntimeException(response.getMsg());
    }

    @RequestMapping("/revoke/{source}/{uuid}")
    @ResponseBody
    public AjaxResult revokeAuth(@PathVariable("source") String source, @PathVariable("uuid") String uuid) throws IOException {
        AuthRequest authRequest = getAuthRequest(source.toLowerCase());
        VsDramasUserOauth vsDramasUserOauth = new VsDramasUserOauth();
        vsDramasUserOauth.setProvider(source);
        vsDramasUserOauth.setOpenid(uuid);
        List<VsDramasUserOauth> oauths = vsDramasUserOauthService.selectVsDramasUserOauthList(vsDramasUserOauth);
//        AuthUser user = userService.getByUuid(uuid);
        if (oauths.isEmpty()) {
            return AjaxResult.error("user does not exist");
        }
        AuthResponse response = null;
        try {
            AuthToken token = new AuthToken();
            token.setAccessToken(oauths.get(0).getAccessToken());
            response = authRequest.revoke(token);
            if (response.ok()) {
                vsDramasUserOauthService.deleteVsDramasUserOauthById(oauths.get(0).getId());
                userService.deleteVsUserById(oauths.get(0).getUserId());
                return AjaxResult.success("用户 [] 的 授权状态 已收回！");
            }
            return AjaxResult.error("用户 [" + oauths.get(0).getNickname() + "] 的 授权状态 收回失败！" + response.getMsg());
        } catch (AuthException e) {
            return AjaxResult.error(e.getErrorMsg());
        }
    }

    @RequestMapping("/refresh/{source}/{uuid}")
    @ResponseBody
    public AjaxResult refreshAuth(@PathVariable("source") String source, @PathVariable("uuid") String uuid) {
        AuthRequest authRequest = getAuthRequest(source.toLowerCase());

//        AuthUser user = userService.getByUuid(uuid);
//        if (null == user) {
//            return AjaxResult.error("用户不存在");
//        }
//        AuthResponse<AuthToken> response = null;
//        try {
//            response = authRequest.refresh(user.getToken());
//            if (response.ok()) {
//                user.setToken(response.getData());
//                userService.save(user);
//                return AjaxResult.success("用户 [" + user.getUsername() + "] 的 access token 已刷新！新的 accessToken: " + response.getData().getAccessToken());
//            }
//            return AjaxResult.error("用户 [" + user.getUsername() + "] 的 access token 刷新失败！" + response.getMsg());
//        } catch (AuthException e) {
//            return AjaxResult.error(e.getMessage());
//        }
        return AjaxResult.success();
    }

    @GetMapping("/registerOther")
    public AjaxResult registerOther(
            @RequestParam("guestUserId") String guestUserId,
            @RequestParam("type") String type,
            @RequestParam("openid") String openid,
            @RequestParam("nickname") String nickname,
            @RequestParam("headimgurl") String headimgurl,
            @RequestParam("email") String email,
            @RequestParam("spm") String spm,
            HttpServletRequest request
    ) {
        VsDramasUserOauth userOauth = new VsDramasUserOauth();
        userOauth.setProvider(type);
        userOauth.setOpenid(openid);
        List<VsDramasUserOauth> userOauths = vsDramasUserOauthService.selectVsDramasUserOauthList(userOauth);
        VsUser vsUser = new VsUser();
        if (userOauths.size() == 0) {
            VsUser user = new VsUser();
            user.setProvider(type);
            user.setSpm(spm);
            user.setKeyword(email);
            String langId = "";
            if (!StringUtils.isEmpty(request.getHeader("Lang-Id"))) {
                langId = request.getHeader("Lang-Id");
            } else {
                langId = "69";
            }

            vsUser = userService.registerUser(user, langId);
            VsDramasUserOauth oauth = new VsDramasUserOauth();
            oauth.setUserId(vsUser.getId());
            String capitalize = WordUtils.capitalize(type);
            oauth.setProvider(capitalize);
            oauth.setPlatform("APP");
            oauth.setOpenid(openid);
            oauth.setNickname(nickname);
            oauth.setHeadimgurl(headimgurl);
            oauth.setCreatetime(System.currentTimeMillis());
            oauth.setUpdatetime(System.currentTimeMillis());
            vsDramasUserOauthService.insertVsDramasUserOauth(oauth);
        } else {
            vsUser = userService.selectVsUserById(userOauths.get(0).getUserId());
        }
        Map map = userService.smsLogin(vsUser);
        return AjaxResult.success(map);
    }

    /**
     * 根据具体的授权来源，获取授权请求工具类
     *
     * @param source
     * @return
     */
    private AuthRequest getAuthRequest(String source) throws AuthException {
        AuthRequest authRequest = null;
        VsDramasConfig vsDramasConfig = new VsDramasConfig();
        vsDramasConfig.setName(source);
        List<VsDramasConfig> configs = vsDramasConfigService.selectVsDramasConfigList(vsDramasConfig);
        String value = configs.get(0).getValue();
        Map<String, String> map = JSON.parseObject(value, new TypeReference<Map<String, String>>(){});
        switch (source.toLowerCase()) {
//            case "alipay":
//                // 支付宝在创建回调地址时，不允许使用localhost或者127.0.0.1，所以这儿的回调地址使用的局域网内的ip
//                authRequest = new AuthAlipayRequest(AuthConfig.builder()
//                        .clientId("")
//                        .clientSecret("")
//                        .alipayPublicKey("")
//                        .redirectUri("https://www.zhyd.me/oauth/callback/alipay")
//                        .build());
//                break;
//            case "qq":
//                authRequest = new AuthQqRequest(AuthConfig.builder()
//                        .clientId("")
//                        .clientSecret("")
//                        .redirectUri("http://localhost:8443/oauth/callback/qq")
//                        .build());
//                break;
//            case "wechat_open":
//                authRequest = new AuthWeChatOpenRequest(AuthConfig.builder()
//                        .clientId("")
//                        .clientSecret("")
//                        .redirectUri("http://www.zhyd.me/oauth/callback/wechat")
//                        .build());
//                break;
            case "google":
                authRequest = new AuthGoogleRequest(AuthConfig.builder()
                        .clientId(map.get("app_id"))
                        .clientSecret(map.get("app_secret"))
                        .redirectUri(map.get("redirect"))
                        .ignoreCheckState(true)
                        .scopes(AuthScopeUtils.getScopes(AuthGoogleScope.USER_EMAIL, AuthGoogleScope.USER_PROFILE, AuthGoogleScope.USER_OPENID))
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
//                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("8.222.181.237", 5303)))
                                .build())
                        .build());
                break;
            case "facebook":
                authRequest = new AuthFacebookRequest(AuthConfig.builder()
                        .clientId(map.get("app_id"))
                        .clientSecret(map.get("app_secret"))
                        .redirectUri(map.get("redirect"))
                        .scopes(AuthScopeUtils.getScopes(AuthFacebookScope.EMAIL))
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
//                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("8.222.181.237", 10080)))
                                .build())
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未获取到有效的Auth配置");
        }
        return authRequest;
    }

}
